#define LOG_TAG "sendevent.c"
#include "../include/mylog.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
//#include <linux/input.h> // this does not compile
#include <errno.h>
#include <time.h>


// from <linux/input.h>

struct input_event {
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
};

typedef struct __my_input_event {
    char *path;
    struct input_event *event;
} my_input_event;

typedef struct __fd_dev_bind {
    int fd;
    char path[PATH_MAX];
}fdev_bind_t;

#define EVIOCGVERSION		_IOR('E', 0x01, int)			/* get driver version */
#define EVIOCGID		_IOR('E', 0x02, struct input_id)	/* get device ID */
#define EVIOCGKEYCODE		_IOR('E', 0x04, int[2])			/* get keycode */
#define EVIOCSKEYCODE		_IOW('E', 0x04, int[2])			/* set keycode */

#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
#define EVIOCGPHYS(len)		_IOC(_IOC_READ, 'E', 0x07, len)		/* get physical location */
#define EVIOCGUNIQ(len)		_IOC(_IOC_READ, 'E', 0x08, len)		/* get unique identifier */

#define EVIOCGKEY(len)		_IOC(_IOC_READ, 'E', 0x18, len)		/* get global keystate */
#define EVIOCGLED(len)		_IOC(_IOC_READ, 'E', 0x19, len)		/* get all LEDs */
#define EVIOCGSND(len)		_IOC(_IOC_READ, 'E', 0x1a, len)		/* get all sounds status */
#define EVIOCGSW(len)		_IOC(_IOC_READ, 'E', 0x1b, len)		/* get all switch states */

#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)		/* get abs value/limits */
#define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)		/* set abs value/limits */

#define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
#define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
#define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */

#define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */

// end <linux/input.h>

// P_RES is a (int64_t *)
#define GET_SYS_TIME(P_RES) do {\
    struct timespec t;\
    if(clock_gettime(CLOCK_MONOTONIC, &t)) {\
        MY_LOGE("clock failure");\
        exit(1);\
    }\
    *P_RES = ((((long long) t.tv_sec) * 1000000000LL) + ((long long) t.tv_nsec)) / 1000 ;\
} while (0)

#define READ_SIZE 8192
typedef struct readbuf_s {
    char *begin;
    char *end;
    char *data_end;
} readbuf_t;

int main(int argc, char *argv[])
{
    int fd_input;
    char *devpath;
    int fd = -1;
    int version;
    int max_len_path = 0;
    char *single_buf = NULL;
    int single_size = 0;
    int num_devs = 0;
    int i = 0;
    fdev_bind_t *fdevs = NULL;

    if(argc != 2) {
        MY_LOGE("usage: %s input_file", argv[0]);
        return 1;
    }

    fd_input = open(argv[1], O_RDWR);
    if(fd_input < 0) {
        MY_LOGE("could not open %s, errormsg:%s", argv[1], strerror(errno));
        return 2;
    }
    if (read(fd_input, &max_len_path, sizeof(typeof(max_len_path))) < 0) {
        MY_LOGE("could not get max_len_path, errormsg:%s", strerror(errno));
        return 3;
    }

    if (read(fd_input, &num_devs, sizeof(typeof(num_devs))) < 0) {
        MY_LOGE("could not get num_devs, errormsg:%s", strerror(errno));
        return 8;
    }

    single_size = max_len_path + sizeof(struct input_event);
   
    //comment
    //MY_LOG("max_len_path = %d", max_len_path);
    //MY_LOG("num_devs = %d", num_devs);
    //MY_LOG("single_size = %d", single_size);

    readbuf_t readbuf;
    readbuf.begin = (char *)malloc(READ_SIZE + single_size);
    readbuf.end = readbuf.begin + (READ_SIZE + single_size);
    readbuf.data_end = readbuf.begin;

    //MY_LOG("readbuf: .begin:%p, .end:%p, .data_end:%p", readbuf.begin, readbuf.end, readbuf.data_end);

    fdevs = (fdev_bind_t *) malloc (sizeof(fdev_bind_t) * num_devs);
    memset(fdevs, 0, sizeof(fdev_bind_t)*num_devs);

    my_input_event *mevent = (my_input_event *) malloc (sizeof(my_input_event));
	//using pointer is enough, no need to alloc/copy
    //mevent->path = (char *) malloc (max_len_path);
    //mevent->event = (struct input_event *) malloc(sizeof(struct input_event));

    int64_t event_first_time = -1; 
    int64_t event_curr_time = -1;
	int64_t sys_curr_time = -1;
	int64_t sys_event_time_dist = -1;

    single_buf = readbuf.begin;
    MY_LOG("Replaying ...");
    while (1) {

        int ret = 0;

        //MY_LOG("single_buf:%p, single_size:%d, readbuf.data_end:%p", single_buf, single_size, readbuf.data_end);
        if(single_buf + single_size > readbuf.data_end) {

            //MY_LOG("memmove(%p, %p, %d)", readbuf.begin, single_buf, readbuf.data_end - single_buf);
            memmove(readbuf.begin, single_buf, readbuf.data_end - single_buf);
            readbuf.data_end = readbuf.begin + (readbuf.data_end - single_buf);

            ret = read(fd_input, readbuf.data_end, READ_SIZE);
            //MY_LOG("read ret: %d, single event size: %d", ret, single_size);
            if (0 == ret) {
                break;
            } else if (ret < 0 ){
                MY_LOGE("read error:%s", strerror(errno));    
                return 4;
            }

            readbuf.data_end = readbuf.data_end + ret;
            single_buf = readbuf.begin;

            MY_ASSERT(readbuf.end >= readbuf.data_end);
        }

        //everything is ok from now on

        mevent->path = single_buf;
        //comment
        //MY_LOG("readed device: %s", mevent->path);
		mevent->event = (typeof(mevent->event))(single_buf+ max_len_path);

        int bfound = 0;
        for ( i = 0; 0!= fdevs[i].fd; i ++) {
            //comment
            //MY_LOG("fdevs[%d].fd=%d, .path=%s", i, fdevs[i].fd, fdevs[i].path);
            if(strcmp(fdevs[i].path, mevent->path) == 0) {
                bfound = 1;
                fd = fdevs[i].fd;
                break;
            }
        }
    
        if(!bfound) {//open a new device
            //comment
            //MY_LOG("new device: %s", mevent->path);

            fd = open(mevent->path, O_RDWR);
            if(fd < 0) {
                MY_LOGE("could not open %s, %s", argv[optind], strerror(errno));
                return 5;
            }
            if (ioctl(fd, EVIOCGVERSION, &version)) {
                MY_LOGE("could not get driver version for %s, %s", argv[optind], strerror(errno));
                continue;
            }

            for( i = 0; i<num_devs; i ++) {
                if(fdevs[i].fd == 0) {
                    fdevs[i].fd = fd;
                    strncpy(fdevs[i].path, mevent->path, max_len_path);
                    break;
                }
            }
            
        } else {
            //comment
            //MY_LOG("already open device: %s", mevent->path);
        }

        //int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
        event_curr_time = mevent->event->time.tv_sec * 1000000LL + mevent->event->time.tv_usec;
		GET_SYS_TIME(&sys_curr_time);
		//MY_LOG("event_curr_time: %lld\tsys_curr_time: %lld\t current_dist: %lld\t", event_curr_time, sys_curr_time, (sys_curr_time - event_curr_time));

		if(-1 == event_first_time) {//first event, cal the time distance
			sys_event_time_dist = sys_curr_time - event_curr_time;
			event_first_time = event_curr_time;
			//MY_LOG("sys_event_time_dist = %lld microseconds", sys_event_time_dist);
		} else {
			int64_t sleep_usecs = -(sys_curr_time - event_curr_time - sys_event_time_dist);
			//MY_LOG("sleep_usecs: %lld microseconds", sleep_usecs);
			if(0 < sleep_usecs) {
				usleep( sleep_usecs);
			}
		}

        //dev's fd got
        ret = write(fd, mevent->event, sizeof(struct input_event));

        if(ret < sizeof(struct input_event)) {
            MY_LOGE("write event failed, %s", strerror(errno));
            continue;
        }

        single_buf += single_size;
    }

    for( i = 0; i<num_devs; i ++) {
        if(fdevs[i].fd != 0) {
            close(fdevs[i].fd);
        }
    }

    free(mevent);
    free(fdevs);

    return 0;
}
